/****************************************************************************
 * Copyright (C) 2011 Maxim Integrated Products, All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED PRODUCTS BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated Products
 * shall not be used except as stated in the Maxim Integrated Products
 * Branding Policy.
 *
 * The mere transfer of this software does not imply any licenses
 * of trade secrets, proprietary technology, copyrights, patents,
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products retains all ownership rights.
 *
 *     Description: MAXQ SPI driver API
 *             $Id: maxq_spi.h 1625 2011-07-01 17:49:20Z jbrodt $
 *
 ********************************************************************************
 */

/** 
* \file
* \brief    Serial peripheral interface (SPI) driver.
* \details  This file defines the driver API including all types and function
*           prototypes.
* \details  All driver function names are prepended by the driver
*           name (spi) and the macro number if multiple SPI macros exist on
*           the microcontroller (e.g. spi1_enable). If the macro number is
*           omitted, the first (0) instance is assumed (e.g. spi_enable =
*           spi0_enabled).
* \details  This driver has configurations in maxq_config.h
* \details  This driver includes optional internal software FIFO management.
*           The functions spi_fifo_sendrecv(), spi_fifo_send() and
*           spi_fifo_recv() are used to interface with the fifo manager.
*           The function spi_handler() manages the FIFO and transmit/receive
*           operations. It should be called from the SPI interrupt service
*           routine. The depth of the transmit and receive FIFOs is configured
*           with the SPIn_RXB_LEN and SPIn_TXB_LEN in maxq_config.h.
* \details  The functions spi_sendrecv(), spi_send() and spi_recv() provide
*           direct access to the SPI hardware buffers and do not use the
*           software FIFO. By removing definitions of SPIn_RXB_LEN and
*           SPIn_TXB_LEN from maxq_config.h, the driver will not allocate any
*           memory for the software FIFO.
* \note     It is not recommended to mix the use of the direct access functions
*           (spi_sendrecv(), spi_send(), spi_recv()) and the FIFO access
*           functions (spi_fifo_sendrecv(), spi_fifo_send(), spi_fifo_recv()).
*/

#ifndef _MAXQ_SPI_H_
#define _MAXQ_SPI_H_

#include "maxq_config.h"

/***** Definitions *****/

// Ensure that driver has at least 1 SPI
/// \cond
#ifndef NUM_SPI
#define NUM_SPI  1
#endif
/// \endcond

// Define register aliases
/// \cond
#if (NUM_SPI == 1)
#define SPICF0 SPICF
#define SPICN0 SPICN
#define SPICK0 SPICK
#define SPIB0 SPIB
#endif
/// \endcond

// Define register aliases
/// \cond
#if (NUM_SPI > 1)
#ifndef SPICN0 
#define SPICF SPICF0
#define SPICN SPICN0
#define SPICK SPICK0
#define SPIB SPIB0
#endif
#endif
/// \endcond

// SPI register bit mask definitions
/// \cond
#define SPI_ENABLE_MASK         0x01  // SPICNn register bit mask
#define SPI_INT_ENABLE_MASK     0x80  // SPICFn register bit mask
/// \endcond

/// \brief Level definition for spi_ssel_callback() parameter
#define SSEL_ACTIVATE     1
/// \brief Level definition for spi_ssel_callback() parameter
#define SSEL_DEACTIVATE   0

/// Valid configuration values for the Mode Fault function
typedef enum { SPI_MODE_FAULT_DISABLED = 0, SPI_MODE_FAULT_ENABLED = 1 } spi_mode_fault_t;

/// Valid configuration values for the serial clock polarity
typedef enum { SPI_POLARITY_RISING = 0, SPI_POLARITY_FALLING = 1 } spi_polarity_t;

/// Valid configuration values for the data phase
typedef enum { SPI_PHASE_ACTIVE = 0, SPI_PHASE_INACTIVE = 1 } spi_phase_t;

/// Valid configuration values for data length (bits)
typedef enum { SPI_LEN_8 = 0, SPI_LEN_16 = 1 } spi_length_t;

/// Valid configuration values for the active level of the slave select input
typedef enum { SPI_SSEL_LOW = 0, SPI_SSEL_HIGH = 1 } spi_ssel_active_t;

/// \brief Configurations used by spi_master_init
/// \sa spi_master_init 
typedef struct {
  spi_mode_fault_t modefault; ///< Mode Fault configuration
  spi_polarity_t polarity;    ///< serial clock polarity
  spi_phase_t phase;          ///< data phase
  spi_length_t length;        ///< data length
  uint8_t baudclkdiv;         ///< clock divider value for desired baudrate \sa spi_get_clkdiv 
} spi_master_config_t;

/// \brief Configurations used by spi_slave_init
/// \sa spi_slave_init 
typedef struct {
  spi_polarity_t polarity;        ///< serial clock polarity
  spi_phase_t phase;              ///< data phase
  spi_length_t length;            ///< data length
  spi_ssel_active_t ssel_active;  ///< active level of the slave select input
} spi_slave_config_t;


/***** Function Prototypes *****/

/**
*   \brief    Calculates the clock divider value for the given baudrate
*   \details  If this functions is called with only immediate value parameters
*             (e.g. spi_get_clkdiv(DEFAULT_SYS_CLK_FREQ, 500000)), it will be
*             calculated at compile time resulting in better execution time
*             performance.
*   \param    clk   SPI source clock frequency in Hz (e.g. #DEFAULT_SYS_CLK_FREQ)
*   \param    baud  desired baudrate in bps
*/
#define spi_get_clkdiv(clk, baud)  ((clk / (2 * baud)) - 1)

/** 
*   \brief    Handler function used to execute driver functionality.
*   \details  This handler function executes the internal driver functionality
*             for the transmit and receive FIFOs. This function can either be
*             called from the SPI interrupt service routine, or directly from
*             the application.
*   \note     This function should be called from the interrupt service routine 
*             if the driver FIFO functions are being used.
*/
void spi_handler(void);

/** 
*   \brief    Initializes the SPI macro in master mode with the the given configuration
*   \param    config   pointer to struct with the desired configuration 
*   \note     #DEFAULT_SYS_CLK_FREQ is used for the baud rate calculation. Use
*             spi_set_baud() to configure the baud rate if a system clock other
*             than the default is being used.
*/
void spi_master_init(spi_master_config_t * config);

/** 
*   \brief    Initializes the SPI macro slave mode with the the given configuration
*   \param    config   pointer to struct with the desired configuration 
*/
void spi_slave_init(spi_slave_config_t * config);

/** 
*   \brief    Deinitializes the SPI
*   \details  This function will wait for an in progress transfer to complete
*             before disabling the SPI.
*/
void spi_deinit(void);

/** 
*   \brief    Configures the SPI baudrate in master mode
*   \details  This function will wait for an in progress transfer to complete
*             before changing the baudrate
*   \param    baudclkdiv  Clock divider value for desired baudrate. Use spi_get_clkdiv().
*/
void spi_change_baud(uint8_t baudclkdiv);

/** 
*   \brief    Enables the SPI interface for communication.
*   \pre      SPI initialization must take place before calling this function.
*/
#define spi_enable()    SPICN|=SPI_ENABLE_MASK

/** 
*   \brief    Disables the SPI interface for communication.
*   \details  This function does not wait for an in progress transfer to
*             complete before disabling the SPI
*/
#define spi_disable()   SPICN&=~SPI_ENABLE_MASK

/** 
*   \brief    Performs a transfer operation (Tx & Rx).
*   \details  A single transfer operation is performed. This function will block
*             until the transfer is complete. For a SPI master, the transfer
*             begins immediately, or after the current transfer completes if
*             another is in progress. For a SPI slave, the transfer begins when
*             the serial clock is received from the master.
*   \details  The transfer complete flag is cleared by this function
*   \note     This function blocks until the transfer completes. There is no
*             timeout.     
*   \param    data    data to send
*   \returns  received data 
*/
uint16_t spi_sendrecv(uint16_t data);

/** 
*   \brief    Initiates a transfer operation.
*   \details  A single transfer operation is initiated with the specified data.
*             This function does not block until the transfer is complete, but
*             block until an in progress transfer has completed.
*   \param    data    data to send
*/
void spi_send(uint16_t data);

/** 
*   \brief    Gets received data
*   \details  This function reads the SPI receive buffer to get the received
*             data from the most recent transfer operation.
*   \details  The transfer complete flag is cleared by this function
*   \returns  received data 
*/
uint16_t spi_recv(void);

/** 
*   \brief    Performs multiple transfer operations (Tx & Rx).
*   \details  The specified number of SPI transfer operations is performed.
*             This function will block until all transfers have completed. For
*             a SPI master, the first transfer begins immediately, or after the
*             current transfer completes if another is in progress. For a SPI
*             slave, the first transfer begins when the serial clock is received
*             from the master. The transfer data is buffered using a software
*             FIFO internal to the SPI driver. The FIFO depths can be configured
*             in the file maxq_config.h. 
*   \details  The transfer complete flag is cleared by this function
*   \details  Calling this function with an rxdata NULL pointer will perform
*             "dummy" reads in which the data is discarded.              
*   \note     This function blocks until all transfers have completed. There is
*             no timeout.     
*   \note     The FIFO is managed by the spi_handler function. This function
*             must be called from the SPI interrupt service routine in the
*             application.
*   \param    txdata  pointer to the data to send
*   \param    rxdata  pointer to where the received data is to be stored
*   \param    len     number of transfer operations to perform
*   \returns  #MAXQ_SUCCESS if successful, #MAXQ_FAILURE otherwise   
*/
int spi_fifo_sendrecv(uint16_t * txdata, uint16_t * rxdata, uint16_t len);

/** 
*   \brief    Initiates multiple transfer operations (Tx & Rx).
*   \details  The specified number of SPI transfer operations is initiated. The
*             transfer data is buffered using a software FIFO internal to the
*             SPI driver. The FIFO depth can be configured in the file
*             maxq_config.h. 
*   \note     The FIFO is managed by the spi_handler function. This function
*             must be called from the SPI interrupt service routine in the
*             application.
*   \param    txdata  pointer to the data to send
*   \param    len     number of transfer operations to perform
*   \returns  #MAXQ_SUCCESS if successful, #MAXQ_FAILURE otherwise   
*/
int spi_fifo_send(uint16_t * txdata, uint16_t len);

/** 
*   \brief    Receives multiple messages
*   \details  This function will attempt the receive the specified number of
*             messages from the software FIFO internal to the SPI driver. This
*             function will not block if the request number of messages have
*             not yet been received.
*   \details  Calling this function with an rxdata NULL pointer will perform
*             "dummy" reads in which the data is discarded.              
*   \note     The FIFO is managed by the spi_handler function. This function
*             must be called from the SPI interrupt service routine in the
*             application.
*   \param    rxdata  pointer to where the received data is to be stored
*   \param    len     number of messages to receive
*   \returns  #MAXQ_SUCCESS if successful, #MAXQ_FAILURE otherwise   
*/
int spi_fifo_recv(uint16_t * rxdata, uint16_t len);

/** 
*   \brief    Get current SPI transfer status
*   \returns  #TRUE if SPI is currently transmitting or messages are waiting
*             in the transmit FIFO, #FALSE otherwise.
*/
int spi_status(void);

/** 
*   \brief    Registers an application callback function
*   \details  The callback function will be called by spi_handler() any time a
*             SPI interrupt is serviced. This function is called before any
*             servicing internal to the SPI driver and allows the application to
*             implement error checking and message handling.
*   \details  Unregistering of the callback can be performed by calling this
*             function function with a NULL parameter.
*   \note     Use of this callback requires that spi_handler() is called for SPI
*             interrupt servicing.
*   \note     The application should clear any flag for which the condition was
*             handled by the callback function. 
*   \param    func  application callback function
*/
void spi_isr_callback(void (*func)(void));

/** 
*   \brief    Registers an application callback function
*   \details  The callback function will be called by spi_handler(), during SPI
*             interrupt servicing any time an error flag is set. This function
*             is called before any servicing internal to the SPI driver.
*   \details  Unregistering of the callback can be performed by calling this
*             function function with a NULL parameter.
*   \note     Use of this callback requires that spi_handler() is called for SPI
*             interrupt servicing.
*   \note     The application should clear any flag for which the condition was
*             handled by the callback function. 
*   \param    func  application callback function
*/
void spi_err_callback(void (*func)(void));

/** 
*   \brief    Registers an application callback function
*   \details  The callback function will be called only after a FIFO operation,
*             typically consisting of multipled transfer operations, has
*             completed.
*   \note     Use of this callback requires that spi_handler() is called for SPI
*             interrupt servicing.
*   \details  Unregistering of the callback can be performed by calling this
*             function function with a NULL parameter.
*   \param    func  application callback function
*/
void spi_fifo_callback(void (*func)(void));

/** 
*   \brief    Registers an application callback function for control of the
*             slave select output by the application.
*   \details  The callback is used only for SPI master channels, and is called
*             before and after any SPI transfer operation. The function
*             allows the application to implement the required slave select
*             output signal level and timing. The driver commands the slave
*             select state via a function paramter. The possible parameter
*             values are defined as #SSEL_ACTIVATE and #SSEL_DEACTIVATE.
*   \details  Unregistering of the callback can be performed by calling this
*             function function with a NULL parameter.
*   \param    func  application callback function
*/
void spi_ssel_callback(void (*func)(int));

/** 
*   \brief    Enables the SPI interrupt localling in the SPI macro
*/
#define spi_enable_interrupt()  SPICF |= SPI_INT_ENABLE_MASK

/** 
*   \brief    Enables the SPI interrupt localling in the SPI macro
*/
#define spi_disable_interrupt() SPICF &= ~SPI_INT_ENABLE_MASK


#if (NUM_SPI > 0)
/// \sa   spi_master_init
void spi0_master_init(spi_master_config_t * config);
/// \sa   spi_slave_init
void spi0_slave_init(spi_slave_config_t * config);
/// \sa   spi_deinit
void spi0_deinit(void);
/// \sa   spi_change_baud
void spi0_change_baud(uint8_t baudclkdiv);
/// \sa   spi_enable 
#define spi0_enable()    SPICN0|=SPI_ENABLE_MASK
/// \sa   spi_disable
#define spi0_disable()   SPICN0&=~SPI_ENABLE_MASK
/// \sa   spi_sendrecv
uint16_t spi0_sendrecv(uint16_t data);
/// \sa   spi_send
void spi0_send(uint16_t data);
/// \sa   spi_recv
uint16_t spi0_recv(void);
/// \sa   spi_fifo_sendrecv
int spi0_fifo_sendrecv(uint16_t * txdata, uint16_t * rxdata, uint16_t len);
/// \sa   spi_fifo_send
int spi0_fifo_send(uint16_t * txdata, uint16_t len);
/// \sa   spi_fifo_recv
int spi0_fifo_recv(uint16_t * rxdata, uint16_t len);
/// \sa   spi_status
int spi0_status(void);
/// \sa   spi_isr_callback
void spi0_isr_callback(void (*func)(void));
/// \sa   spi_err_callback
void spi0_err_callback(void (*func)(void));
/// \sa   spi_fifo_callback
void spi0_fifo_callback(void (*func)(void));
/// \sa   spi_ssel_callback
void spi0_ssel_callback(void (*func)(int));
/// \sa   spi_enable_interrupt
#define spi0_enable_interrupt()   SPICF0 |= SPI_INT_ENABLE_MASK
/// \sa   spi_disable_interrupt
#define spi0_disable_interrupt()  SPICF0 &= ~SPI_INT_ENABLE_MASK

// Function aliases
/// \cond 
#define spi_master_init     spi0_master_init
#define spi_slave_init      spi0_slave_init
#define spi_deinit          spi0_deinit
#define spi_change_baud     spi0_change_baud
#define spi_sendrecv        spi0_sendrecv
#define spi_send            spi0_send
#define spi_recv            spi0_recv
#define spi_status          spi0_status
#define spi_isr_callback    spi0_isr_callback
#define spi_fifo_callback   spi0_fifo_callback
#define spi_ssel_callback   spi0_ssel_callback
/// \endcond
#endif

#if (NUM_SPI > 1)
/// \sa   spi_master_init
void spi1_master_init(spi_master_config_t * config);
/// \sa   spi_slave_init
void spi1_slave_init(spi_slave_config_t * config);
/// \sa   spi_deinit
void spi1_deinit(void);
/// \sa   spi_change_baud
void spi1_change_baud(uint8_t baudclkdiv);
/// \sa   spi_enable
#define spi1_enable()    SPICN1|=SPI_ENABLE_MASK
/// \sa   spi_disable
#define spi1_disable()   SPICN1&=~SPI_ENABLE_MASK
/// \sa   spi_sendrecv
uint16_t spi1_sendrecv(uint16_t data);
/// \sa   spi_send
void spi1_send(uint16_t data);
/// \sa   spi_recv
uint16_t spi1_recv(void);
/// \sa   spi_fifo_sendrecv
int spi1_fifo_sendrecv(uint16_t * txdata, uint16_t * rxdata, uint16_t len);
/// \sa   spi_fifo_send
int spi1_fifo_send(uint16_t * txdata, uint16_t len);
/// \sa   spi_fifo_recv
int spi1_fifo_recv(uint16_t * rxdata, uint16_t len);
/// \sa   spi_status
int spi1_status(void);
/// \sa   spi_isr_callback
void spi1_isr_callback(void (*func)(void));
/// \sa   spi_err_callback
void spi1_err_callback(void (*func)(void));
/// \sa   spi_fifo_callback
void spi1_fifo_callback(void (*func)(void));
/// \sa   spi_ssel_callback
void spi1_ssel_callback(void (*func)(int));
/// \sa   spi_enable_interrupt
#define spi1_enable_interrupt()   SPICF1 |= SPI_INT_ENABLE_MASK
/// \sa   spi_disable_interrupt
#define spi1_disable_interrupt()  SPICF1 &= ~SPI_INT_ENABLE_MASK
#endif

#endif /* _MAXQ_SPI_H_ */
